package com.vhukze.basics.util;

import cn.hutool.core.io.IoUtil;
import cn.hutool.core.io.file.FileNameUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.apache.poi.ss.usermodel.CellStyle;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.CodeSignature;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.function.*;

/**
 * @author vhukze
 * @date 2021/11/3 11:46
 */
public class MyUtils {


    /**
     * 导出Excel封装操作
     *
     * @param response response对象
     * @param fileName 导出的文件名称
     * @param isBig    是否大文件（数据非常多）
     * @param consumer 实际的写数据操作
     */
    public static void exportExcel(HttpServletResponse response, String fileName, Boolean isBig, Consumer<ExcelWriter> consumer) throws IOException {

        ExcelWriter writer;
        if (isBig) {
            writer = ExcelUtil.getBigWriter();
        } else {
            writer = ExcelUtil.getWriter(true);
        }

        CellStyle headCellStyle = writer.getHeadCellStyle();
        headCellStyle.setWrapText(true);

        // 设计列宽
        for (int i = 0; i < 15; i++) {
            writer.setColumnWidth(i, 15);
        }
        // 设置行高
        for (int i = 0; i < 4; i++) {
            writer.setRowHeight(i, 40);
        }

        // 设置自动换行
        writer.getStyleSet().setWrapText();

        // 写数据操作
        consumer.accept(writer);

        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
        response.setHeader("Content-Disposition", "attachment;filename=" +
                new String(fileName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1));

        ServletOutputStream out = response.getOutputStream();
        writer.flush(out, true);

        writer.close();
        IoUtil.close(out);
    }

    /**
     * 获取参数Map集合
     */
    public static Map<String, Object> aopGetNameAndValue(JoinPoint joinPoint) {
        Map<String, Object> param = new HashMap<>();
        Object[] paramValues = joinPoint.getArgs();
        String[] paramNames = ((CodeSignature) joinPoint.getSignature()).getParameterNames();
        for (int i = 0; i < paramNames.length; i++) {
            param.put(paramNames[i], paramValues[i]);
        }
        return param;
    }

    /**
     * 获取用户真实IP地址，不使用request.getRemoteAddr();的原因是有可能用户使用了代理软件方式避免真实IP地址,
     * <p>
     * 可是，如果通过了多级反向代理的话，X-Forwarded-For的值并不止一个，而是一串IP值，究竟哪个才是真正的用户端的真实IP呢？
     * 答案是取X-Forwarded-For中第一个非unknown的有效IP字符串。
     * <p>
     * 如：X-Forwarded-For：192.168.1.110, 192.168.1.120, 192.168.1.130,
     * 192.168.1.100
     * <p>
     * 用户真实IP为： 192.168.1.110
     */
    public static String getIpAddress(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_CLUSTER_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_FORWARDED");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_VIA");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("REMOTE_ADDR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        if (ip.contains(",")) {
            return ip.split(",")[0];
        } else {
            return ip;
        }

    }

    /**
     * 通用下载文件方法
     *
     * @param request  request对象
     * @param response response对象
     * @param path     文件存储的位置
     * @param fileName 下载的文件名称
     */
    public static void downloadFile(HttpServletRequest request, HttpServletResponse response, String path, String fileName) throws IOException {

        response.setContentType(request.getSession().getServletContext().getMimeType(FileNameUtil.extName(fileName))); // 获取文件的mimetype
        response.setHeader("content-disposition", "attachment;fileName=" + URLEncoder.encode(fileName, "UTF-8"));

        response.setContentType("application/octet-stream;charset=utf-8");
        response.setHeader("Content-Disposition", "attachment;filename=" +
                new String(fileName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1));

        FileInputStream in = new FileInputStream(path);

        ServletOutputStream out = response.getOutputStream();
        IoUtil.copy(in, out);

        IoUtil.close(in);
        IoUtil.close(out);
    }

    /**
     * 通用查询列表操作封装
     *
     * @param pageNo   页码
     * @param pageSize 每页条数
     * @param su       查询列表操作
     * @param func     列表转换
     * @author vhukze
     * @date 2022/8/8 11:25
     */
    public static <T, R> Map<String, Object> queryList(Integer pageNo, Integer pageSize, Supplier<List<T>> su,
                                                       Function<List<T>, List<R>> func) {
        Map<String, Object> map = new HashMap<>();

        if (pageNo != null && pageSize != null) {
            PageHelper.startPage(pageNo, pageSize);
        }
        List<T> respList = su.get();
        PageInfo<T> pageInfo = new PageInfo<>(respList);

        map.put("list", func != null ? func.apply(pageInfo.getList()) : pageInfo.getList());
        map.put("total", pageInfo.getTotal());
        return map;
    }

    /**
     * 求百分比
     *
     * @param total 总数
     * @param num   除数
     * @param scale 保留小数
     */
    public static BigDecimal percent(BigDecimal total, Integer num, int scale) {
        return total == null || total.intValue() == 0 ? BigDecimal.valueOf(0) :
                BigDecimal.valueOf(num).divide(total, scale + 2, RoundingMode.HALF_UP)
                        .multiply(BigDecimal.valueOf(100)).setScale(scale, RoundingMode.HALF_UP);
    }
}
